package ws

import (
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"gogogo-server/engine/lgconf"
	"net"
	"net/http"
	"sync"
	"time"
)

// LGServer websocket的服务端 LetGoServer
type LGServer struct {
	cfg            *lgconf.LGServerConfig
	allConns       TypeMapConns       // 所有连接存储
	upgrader       websocket.Upgrader // 升级websocket
	fnNewGateAgent TypeFnNewAgent     // 连接上来后,创建GateAgent回调
	mu             sync.Mutex         // 因为每个连接上来都是一个协程,所以需要写成员函数时需要锁
	wg             sync.WaitGroup     // 每个连接销毁了-1 ,创建时+1
	service        http.Server        // http的服务器
}

func (lgs *LGServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "仅支持GET连接", http.StatusMethodNotAllowed)
		return
	}
	conn, err := lgs.upgrader.Upgrade(w, r, nil)
	if err != nil {
		zap.S().Warnf("升级websocket出错:%s, Addr:%s", err, r.RemoteAddr)
		http.Error(w, "连接失败", http.StatusUpgradeRequired)
		return
	}
	conn.SetReadLimit(int64(lgs.cfg.MaxMsgLen))
	lgs.wg.Add(1)
	defer lgs.wg.Done()
	lgs.mu.Lock()
	if lgs.allConns == nil { // 刚好关闭的时候,还有连接进来,很极限
		lgs.mu.Unlock()
		conn.Close()
		return
	}
	if len(lgs.allConns) >= lgs.cfg.MaxConnNum {
		lgs.mu.Unlock()
		conn.Close()
		return
	}
	lgs.allConns[conn] = nil
	lgs.mu.Unlock()
	lgConn := NewConn(conn, lgs.cfg.MaxMsgLen, lgs.cfg.SizeWriteChan)
	ag := lgs.fnNewGateAgent(lgConn)
	ag.AgentRun() //跑起来,读消息

	// 结束断开
	lgConn.Close()
	lgs.mu.Lock()
	delete(lgs.allConns, conn)
	lgs.mu.Unlock()
	ag.AgentClose()
}

// NewServer 创建一个新的ws服务 wsAddr: ip:port
func NewServer(cfg *lgconf.LGServerConfig, fn TypeFnNewAgent) *LGServer {
	if cfg.Timeout < 1 {
		cfg.Timeout = 10 * time.Second
		zap.S().Warnf("[%s]超时时间修正为:%d", cfg.Name, cfg.Timeout)
	}
	if cfg.MaxMsgLen < 1 {
		cfg.MaxMsgLen = 1000000
		zap.S().Warnf("[%s]超时消息长度修正为:%d", cfg.Name, cfg.MaxMsgLen)
	}
	if cfg.SizeWriteChan < 1 {
		cfg.SizeWriteChan = 100000
		zap.S().Warnf("[%s]消息队列长度修正为:%d", cfg.Name, cfg.SizeWriteChan)
	}
	ws := &LGServer{
		cfg:            cfg,
		fnNewGateAgent: fn,
		allConns:       make(TypeMapConns),
	}
	ws.service = http.Server{
		Addr:              cfg.Addr,
		ReadHeaderTimeout: cfg.Timeout,
		ReadTimeout:       cfg.Timeout,
		WriteTimeout:      cfg.Timeout,
		MaxHeaderBytes:    1024,
		Handler:           ws,
	}
	return ws
}

func (lgs *LGServer) Start() {
	listen, err := net.Listen("tcp", lgs.service.Addr)
	if err != nil {
		zap.S().Fatalf("监听:%s 失败:%s", lgs.service.Addr, err)
	}
	go lgs.service.Serve(listen)
}

func (lgs *LGServer) Close() {
	lgs.service.Close()
	lgs.mu.Lock()
	for conn := range lgs.allConns {
		conn.Close()
	}
	lgs.allConns = nil
	lgs.mu.Unlock()
	lgs.wg.Wait()
}
